package com.agh.portal.domain.util;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.velocity.tools.config.DefaultKey;

/**
 * This is modified version of VelocityTools 1.2
 * https://velocity.apache.org/tools/devel/javadoc/org/apache/velocity/tools/generic/SortTool.html
 * Author: S. Brett Sutton, Nathan Bubna
 * 
 * I'm not author of it, I modified it only for purpose of this project.
 */

@DefaultKey("sorter")
public class SortTool {
	public Collection sort(Collection collection) {
		return sort(collection, (List) null);
	}

	public Collection sort(Object[] array) {
		return sort(array, (List) null);
	}

	public Collection sort(Map map) {
		return sort(map, (List) null);
	}

	public Collection sort(Object object, String property) {
		List properties = new ArrayList(1);
		properties.add(property);

		if (object instanceof Collection) {
			return sort((Collection) object, properties);
		}
		if (object instanceof Object[]) {
			return sort((Object[]) (Object[]) object, properties);
		}
		if (object instanceof Map) {
			return sort((Map) object, properties);
		}

		return null;
	}

	public Collection sort(Collection collection, List properties) {
		List list = new ArrayList(collection.size());
		list.addAll(collection);
		return internalSort(list, properties);
	}

	public Collection sort(Map map, List properties) {
		return sort(map.values(), properties);
	}

	public Collection sort(Object[] array, List properties) {
		return internalSort(Arrays.asList(array), properties);
	}

	protected Collection internalSort(List list, List properties) {
		try {
			if (properties == null) {
				Collections.sort(list);
			} else
				Collections.sort(list, new PropertiesComparator(properties));

			return list;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	protected static Comparable getComparable(Object object, String property) {
		try {
			return ((Comparable) PropertyUtils.getProperty(object, property));
		} catch (Exception e) {
//			System.out.println("Could not retrieve comparable value for '" + property + "' from " + object + ": " + e);
			return null;
		}
	}

	public static class PropertiesComparator implements Comparator, Serializable {
		private static final int TYPE_ASCENDING = 1;
		private static final int TYPE_DESCENDING = -1;
		public static final String TYPE_ASCENDING_SHORT = "asc";
		public static final String TYPE_DESCENDING_SHORT = "desc";
		List properties;
		int[] sortTypes;

		public PropertiesComparator(List props) {
			this.properties = new ArrayList(props.size());
			this.properties.addAll(props);

			this.sortTypes = new int[this.properties.size()];

			for (int i = 0; i < this.properties.size(); ++i) {
				if (this.properties.get(i) == null) {
					throw new IllegalArgumentException("Property " + i + "is null, sort properties may not be null.");
				}

				String prop = this.properties.get(i).toString();
				int colonIndex = prop.indexOf(58);
				if (colonIndex != -1) {
					String sortType = prop.substring(colonIndex + 1);
					this.properties.set(i, prop.substring(0, colonIndex));

					if ("asc".equalsIgnoreCase(sortType)) {
						this.sortTypes[i] = 1;
					} else if ("desc".equalsIgnoreCase(sortType)) {
						this.sortTypes[i] = -1;
					} else {
						this.sortTypes[i] = 1;
					}

				} else {
					this.sortTypes[i] = 1;
				}
			}
		}

		public int compare(Object lhs, Object rhs) {
			for (int i = 0; i < this.properties.size(); ++i) {
				int comparison = 0;
				String property = (String) this.properties.get(i);

				Comparable left = SortTool.getComparable(lhs, property);
				Comparable right = SortTool.getComparable(rhs, property);
				
				if(left == null && right == null){
					comparison = 0;
				}else if(left == null && right != null){
					comparison *= -1;
				}else if(right == null && left != null){
					comparison *= 1;
				}else if(left instanceof String){
					comparison = ((String) left).compareToIgnoreCase((String) right);
				}else{
					comparison = left.compareTo(right);
				}

				if (comparison != 0) {
					return (comparison * this.sortTypes[i]);
				}
			}
			return 0;
		}
	}
}